home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 37
/
Aminet 37 (2000)(Schatztruhe)[!][Jun 2000].iso
/
Aminet
/
util
/
cdity
/
DetotterButton.lha
/
DetotterButton
/
DetotterMouseButton.mod
< prev
next >
Wrap
Text File
|
2000-03-23
|
14KB
|
363 lines
|##########|
|#MAGIC #|GNOLIEBE
|#PROJECT #|"DetotterMouseButton"
|#PATHS #|"StdProject"
|#LINK #|"c:"
|#GUIDE #|""
|#STACK #|"4096"
|#FLAGS #|xx-x-x--xxx-xxx-x---------------
|#USERSW #|-x------------------------------
|#USERMASK#|-x------------------------------
|#SWITCHES#|xx---xxxxx-xx---
|##########|
MODULE DetotterMouseButton;
(**************************************************************************
*
* DetotterMouseButton.mod
* based on Swap_Buttons.c from the Amiga developer CD V1.1
*
* This detotters a button of an old mouse.
* The Cluster code installs and removes the input.device handler and
* manages the timer messages.
*
* The handler is written in assembly code since it is important that
* handlers be as fast as possible while processing the input events.
*
*)
$$IF Final THEN
$$RangeChk := FALSE
$$OverflowChk := FALSE
$$ReturnChk := FALSE
$$StrZeroChk := FALSE
$$StackChk := FALSE
$$NilChk := FALSE
$$Debug := FALSE
$$EntryCode := FALSE
$$END
FROM Input IMPORT All;
FROM System IMPORT Regs;
FROM Exec IMPORT MsgGrp, InterruptPtr, SignalGrp, LibraryPtr;
TYPE
DetotterDataPtr = POINTER TO DetotterData;
DetotterData =
RECORD
execBase : LibraryPtr; | Fast access to the ExecBase
delayedEvent : InputEvent; | Storage for the delayed release event
delayed : BOOLEAN; | Is delayedEvent valid?
qualifier : Qualifiers; | Qualifier bit for the processed mouse button
pressed : QualifierSet; | The qualifier bit is set if the virtual button is pressed
rawCode : CARDINAL; | Raw key code for the processed button
task : TaskPtr; | Main task
abortDelaySigSet,
delaySigSet : TaskSigSet; | Signals the main task listens to
END;
$$RangeChk := FALSE
$$OverflowChk := FALSE
$$ReturnChk := FALSE
$$StrZeroChk := FALSE
$$StackChk := FALSE
$$NilChk := FALSE
$$Debug := FALSE
$$EntryCode := FALSE
PROCEDURE Detotter (event IN A0 : InputEventPtr;
data IN A1 : DetotterDataPtr) : InputEventPtr;
VAR
code IN D2 : CARDINAL;
qualifier IN D3 : QualifierSet;
|
| The event list gets passed to you in a0.
| The InputHandler.data field is passed to you in a1.
|
| On exit you must return the event list in d0. In this way
| you could add or remove items from the event list.
|
CONST
Signal = -324;
buttonMask = CAST(QualifierSet,INTEGER(-1)) - QualifierSet:{leftButton, midButton, rightButton};
| Compiler doesn't like this
|detotterDataPtr = DetotterDataPtr(NIL);
|delayedEventOffset = detotterDataPtr.delayedEvent'PTR;
|delayedEventOffset = DetotterDataPtr(NIL).delayedEvent'ADR;
BEGIN
ASSEMBLE (
_Detotter:
move event,-(a7) | Save the event list
move.l d2-d3/a2,-(a7) | Save other registers
|
| Since the event list could be a linked list, we start a loop
| here to handle all of the events passed to us.
|
CheckLoop:
|
| The actual button up/down events are transmitted as the
| code field in RAWMOUSE events. The code field must the be
| checked and modified when needed on RAWMOUSE events. If the
| event is not a RAWMOUSE, we are done with it.
|
move event.qualifier,qualifier | Get qualifier set ...
move event.code,code | Get code ...
cmp #Class.rawmouse,event.class | Check for mouse
bne FixQualifiers | If not, next...
move code,d0 | Duplicate ...
bclr #7,d0 | Mask UP_PREFIX
cmp data.rawCode,d0 | Check for button
bne FixQualifiers | No button to care about
|(* begin of debug block, if block is commented out, the thing should not crash
| The button to detotter has changed
btst #7,code | Is it pressed ?
beq ButtonPressed
ButtonReleased: | Button is physically released
lea data.delayedEvent,a2
cmp a2,event | Is this the message we delayed?
beq PassDeferedRelease | If yes, pass it without delaying again
tst data.delayed | Is there a event delayed, already?
bne RedeferRelease | Yes seems so
DeferRelease: | A new release event occured
| InputEvent'SIZE = 22
move event,-(a7)
|lea data.delayedEvent,a2 | We have done this some lines before already
move.l (event)+,(a2)+ | Store this event
move.l (event)+,(a2)+
move.l (event)+,(a2)+
move.l (event)+,(a2)+
move.l (event)+,(a2)+
move.w (event)+,(a2)+
move (a7)+,event
move #-1,data.delayed | Mark this copy as valid
RedeferRelease:
| There is already a deferred release event, flush the current only
SignalMainTask: | Tell the main task, that it has to start a timer
move.l a0/a1/a6,-(a7)
move.l data.execBase,a6
move.l data.delaySigSet,d0
move.l data.task,a1 | Since data = a1 we have to write this last
jsr Signal(a6) | Signal (main task, delay signal);
move.l (a7)+,a0/a1/a6
bra FlushMessage | Trash this message, since it is either stored or double
PassDeferedRelease: | We shall pass this message without parsing
clr data.delayed | This stops the delay state
clr data.pressed | Let's clear the corresponding qualifier bit in our internal qualifier set
bra NextEvent | This hasn't to be processed anymore, qualifiers are already correct
ButtonPressed:
tst data.delayed | Do we hold a delayed release event?
bne AbortDelay | Yes, so we will flush this and the delayed event
move data.qualifier,d0 | No, so we pass the button press event
clr.w d1 | and refresh the internal qualifier set
bset d0,d1 | according to this change
move d1,data.pressed
bra FixQualifiers
AbortDelay:
clr data.delayed | This stops the delay state, too
move.l a0/a1/a6,-(a7)
move.l data.execBase,a6
move.l data.abortDelaySigSet,d0
move.l data.task,a1 | Since data = a1 we have to write this last
jsr Signal(a6) | Signal (main task, delay abort signal);
move.l (a7)+,a0/a1/a6
FlushMessage:
move #Input.noButton,code | Setting code to noButton turns this message into a dummy event
bra StoreEvent | This hasn't to be processed anymore, qualifiers are already correct
| end of block *)
|
| Since we are changing mouse button events, we need to make
| sure that we change the qualifiers on all of the messages. The
| mouse buttons are tracked in the message qualifiers
| for use in such things as dragging. To make sure that we continue
| to drag correctly, we change the qualifiers.
|
FixQualifiers:
move data.qualifier,d0
bclr d0,qualifier
or data.pressed,qualifier | Set the processed qualifier to the emulated state
StoreEvent:
move code,event.code | Save back...
move qualifier,event.qualifier
|move.l #$123134,0 | provoke Enforcer
|
| The event list is linked via a pointer to the next event
| in the first element of the structure. That is why it is not
| nessesary to use: move.l ie_NextEvent(a0),d0
|
| The reason I move to d0 first is that this also checks for zero.
| The last event in the list will have a NULL ie_NextEvent field.
| This is NOT as standard EXEC list where the node after the last
| node is NULL. Input events are single-linked for performance.
|
NextEvent:
move event.nextEvent,d0 | Get next event, check = NIL
move d0,event | Into a0 ...
bne CheckLoop | Do some more.
|
| All done, just return the event list... (in d0)
|
move.l (a7)+,d2-d3/a2 | Restore other registers
move.l (a7)+,d0 | Get event list back...
rts | Return from handler...
);
END Detotter;
$$RangeChk := OLD
$$OverflowChk := OLD
$$ReturnChk := OLD
$$StrZeroChk := OLD
$$StackChk := OLD
$$NilChk := OLD
$$Debug := OLD
FROM Resources IMPORT New, MemReqs;
FROM T_Exec IMPORT NoFreeSignal;
FROM Timer IMPORT All;
VAR
inputReqBlk : IOInputPtr;
releaseTimer : IOTimerPtr;
inputHandler : InterruptPtr;
abortDelaySignal,
delaySignal : TaskSignals;
timerSigSet := TaskSigSet:{};
recSigSet : TaskSigSet;
waitSigSet := TaskSigSet:{ctrlC};
CONST
DefaultValues = ARRAY OF LONGINT : (0, 20, 100);
FROM Dos IMPORT VPrintf;
FROM CLIStartup IMPORT All;
TYPE
LongPtr = POINTER TO LONGINT;
ArgRec = RECORD
button : LongPtr;
delay : LongPtr;
pri : LongPtr;
END;
VAR
args := ArgRec : (DefaultValues[0]'PTR, DefaultValues[1]'PTR, DefaultValues[2]'PTR);
BEGIN
ReadArgs ("BUTTON/N,DELAY/N,PRIORITY=PRI/N", args);
IF args.button^ NOT OF 0..2 THEN
FORGET VPrintf ("BUTTON must be of 0..2."+&10);
RAISE (ScriptError);
END;
IF args.delay^ <= 0 THEN
FORGET VPrintf ("DELAY must be positive."+&10);
RAISE (ScriptError);
END;
IF args.pri^ NOT OF SHORTINT'MIN..SHORTINT'MAX THEN
FORGET VPrintf ("PRIORITY must be of -128..127."+&10);
RAISE (ScriptError);
END;
New (inputHandler, mem := {public, clear});
inputHandler.code := PROC (Detotter);
inputHandler.pri := args.pri^;
inputHandler.name := "Detotter mouse button";
WITH DetotterDataPtr (inputHandler.data) AS data DO
New (data, mem := {public, clear});
delaySignal := AllocSignal(anySignal); ASSERT (#, NoFreeSignal);
abortDelaySignal := AllocSignal(anySignal); ASSERT (#, NoFreeSignal);
INCL (data. delaySigSet, delaySignal); INCL (waitSigSet, delaySignal);
INCL (data.abortDelaySigSet, abortDelaySignal); INCL (waitSigSet, abortDelaySignal);
data.task := TaskPtr (System.OwnTask);
data.execBase := Exec.ExecBase;
data.qualifier := leftButton; DEC (data.qualifier, args.button^);
data.rawCode := Input.lButton; INC (data.rawCode, args.button^);
END;
releaseTimer := OpenTimer (microHz);
INCL (waitSigSet, releaseTimer.replyPort.sigBit);
INCL (timerSigSet, releaseTimer.replyPort.sigBit);
inputReqBlk := OpenInput();
inputReqBlk.data := inputHandler;
inputReqBlk.command := Input.addHandler;
FORGET DoIO (inputReqBlk);
REPEAT
recSigSet := Wait (waitSigSet);
|WriteFormat ("signals %8lx received"+&10, data := CAST(LONGINT, recSigSet));
WITH DetotterDataPtr (inputHandler.data) AS data DO
|WriteFormat ("delayed event(%ld), interim check, rawCode %ld, qualifiers %4lx"+&10,
|data := CAST(SHORTINT,data.delayed), data.delayedEvent.code, CAST (CARDINAL, data.delayedEvent.qualifier));
(* Delay signal and abort delay signal may arrive at the same time,
and you will not know, which event was the first.
It depends on the order of the following two if statements,
whether mouse will emulated as pressed or as released in this case.
*)
IF delaySignal IN recSigSet THEN
|WriteString ("delay release"+&10);
IF CheckIO (releaseTimer)=NIL THEN
AbortIO (releaseTimer);
| do not WaitIO() here, this will lock the program
END;
| it's important to set this here, you cannot re-use this values
releaseTimer.command := addRequest;
releaseTimer.time.secs := args.delay^ DIV 1000;
releaseTimer.time.micro := (args.delay^ MOD 1000) * 1000;
SendIO (releaseTimer);
END;
IF abortDelaySignal IN recSigSet THEN
|WriteString ("abort delay"+&10);
IF CheckIO (releaseTimer)=NIL THEN
AbortIO (releaseTimer);
| make sure, that the final release caused by a returning timer message is supressed
FORGET WaitIO (releaseTimer);
FORGET SetSignal ({}, timerSigSet);
END;
END;
IF releaseTimer.replyPort.sigBit IN recSigSet THEN
|WriteString ("finally release"+&10);
FORGET WaitIO (releaseTimer);
FORGET SetSignal ({}, timerSigSet);
inputReqBlk.data := data.delayedEvent'PTR;
inputReqBlk.length := InputEvent'SIZE;
inputReqBlk.command := Input.writeEvent;
FORGET DoIO (inputReqBlk);
END;
END;
UNTIL ctrlC IN recSigSet; | unnecessary, Cluster installs a SoftException for CtrlC
CLOSE
IF inputReqBlk#NIL THEN
inputReqBlk.data := inputHandler;
inputReqBlk.command := Input.remHandler;
FORGET DoIO (inputReqBlk);
END;
IF delaySignal # noSignal THEN FreeSignal ( delaySignal) END;
IF abortDelaySignal # noSignal THEN FreeSignal (abortDelaySignal) END;
IF CheckIO (releaseTimer)=NIL THEN AbortIO (releaseTimer) END;
| according to AutoDocs WaitIO _is_ _necessary_,
| but if the Request wasn't used ever, this blocks the cleanup :-(
|FORGET WaitIO (releaseTimer);
|FORGET GetMsg (releaseTimer.replyPort);
END DetotterMouseButton.